/**
 * 
 */
package idv.takeshi.software.productline.bookstore.application.service.spring;

import idv.takeshi.software.productline.bookstore.application.service.MailService;
import idv.takeshi.software.productline.bookstore.application.service.PlaceOrderService;
import idv.takeshi.software.productline.bookstore.domain.model.book.Book;
import idv.takeshi.software.productline.bookstore.domain.model.book.BookRepository;
import idv.takeshi.software.productline.bookstore.domain.model.category.Category;
import idv.takeshi.software.productline.bookstore.domain.model.category.CategoryRepository;
import idv.takeshi.software.productline.bookstore.domain.model.deliveryservice.DeliveryService;
import idv.takeshi.software.productline.bookstore.domain.model.deliveryservice.DeliveryServiceRepository;
import idv.takeshi.software.productline.bookstore.domain.model.order.Invoice;
import idv.takeshi.software.productline.bookstore.domain.model.order.Order;
import idv.takeshi.software.productline.bookstore.domain.model.order.OrderDuplicateException;
import idv.takeshi.software.productline.bookstore.domain.model.order.OrderFactory;
import idv.takeshi.software.productline.bookstore.domain.model.order.OrderItem;
import idv.takeshi.software.productline.bookstore.domain.model.order.OrderRepository;
import idv.takeshi.software.productline.bookstore.domain.model.order.OrderStatus;
import idv.takeshi.software.productline.bookstore.domain.model.order.Payment;
import idv.takeshi.software.productline.bookstore.domain.model.useraccount.Clerk;
import idv.takeshi.software.productline.bookstore.domain.model.useraccount.Customer;
import idv.takeshi.software.productline.bookstore.domain.model.useraccount.UserAccount;
import idv.takeshi.software.productline.bookstore.domain.model.useraccount.UserAccountRepository;
import idv.takeshi.software.productline.bookstore.utils.template.TemplateUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.mail.MessagingException;

import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * A service implementation for placing order operation.
 * @author takeshi
 *
 */
@Service("placeOrderService")
@Transactional
public class PlaceOrderServiceImpl implements PlaceOrderService {
	
	private static final Log log = LogFactory.getLog(PlaceOrderServiceImpl.class);
	
	@Autowired
	private DeliveryServiceRepository deliveryServiceRepository;
	
	@Autowired
	private MailService mailService;
	
	@Autowired
	private UserAccountRepository userAccountRepository;
	
	@Autowired
	private CategoryRepository categoryRepository;
	
	@Autowired
	private OrderFactory orderFactory;
	
	@Autowired
	private OrderRepository orderRepository;
	
	@Autowired
	private BookRepository bookRepository;
	
	@Autowired
	private MessageSource messageSource;
	
	private String templatePath;
	
	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.application.service.PlaceOrderService#getDeliveryServiceById(java.lang.Integer)
	 */
	public DeliveryService getDeliveryServiceById(Integer id) {
		return this.deliveryServiceRepository.findById(id);
	}
	
	
	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.application.service.PlaceOrderService#persistNewOrder(idv.takeshi.software.productline.bookstore.domain.model.useraccount.UserAccount, idv.takeshi.software.productline.bookstore.domain.model.order.Order, java.util.Locale)
	 */
	public void persistNewOrder(Long userAccountId, Order newOrder, Locale locale) 
		throws OrderDuplicateException{
		
		Validate.notNull(userAccountId, "userAccount shall always not be null");
		Validate.notNull(newOrder, "newOrder shall always not be null");
		Validate.notNull(locale, "locale shall always not be null");
		Book book = null;
		UserAccount userAccount = this.userAccountRepository.findById(userAccountId);
		
		//TODO
		//for transaction detached object issue
//		newOrder.setDeliveryService(this.deliveryServiceRepository.findById(
//				newOrder.getDeliveryService().getId()));
//		for(OrderItem orderItem : newOrder.getOrderItems()){
//			book = this.bookRepository.findById(orderItem.getOrderedBook().getId());
//			orderItem.setOrderedBook(book);
//		}
//		
//		//check for this order and it's inventories status
		newOrder.checkOrderItemsStatus();
		
		this.orderRepository.add(newOrder.getPayment());
		this.orderRepository.add(newOrder.getInvoice());
		this.orderRepository.add(newOrder);
		
		userAccount.getRole(Customer.class).addOrder(newOrder);
		
		//for transaction detached object issue
		for(OrderItem item : newOrder.getOrderItems()){
			book = item.getOrderedBook();
			this.bookRepository.update(book);
		}
//		this.userAccountRepository.update(userAccount);
		
		sendNoticeMail(userAccount, newOrder, locale);
	}
	
	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.application.service.PlaceOrderService#getOrderById(java.lang.Long)
	 */
	public Order getOrderById(Long orderId) {
		return this.orderRepository.findById(orderId);
	}


	private void sendNoticeMail(UserAccount userAccount, Order order, Locale locale){
		Map<String, Object> root = new HashMap<String, Object>();
		String subjectCode = null;
		String subject = null;
		String mailContent = null;
		
		if(OrderStatus.NEW == order.getStatus()){
			subjectCode = "mail.order.new.subject";
		} else if(OrderStatus.WAIT_FOR_REPLENISHMENT == order.getStatus()){
			subjectCode = "mail.order.waitForReplenishment.subject"; 
		} else{
			throw new RuntimeException("undefied order status:" + order.getStatus());
		}
		subject = this.messageSource.getMessage(subjectCode, new String[]{"" + order.getId()}, locale);
		
		root.put("userName", userAccount.getUserName());
		root.put("order", order);
		mailContent = TemplateUtils.generateTemplateString(this.templatePath, "placeOrder.ftl", root, locale);
		
		List<String> clerkEmailAddresses = this.userAccountRepository.findEmailAddressesByRole(Clerk.class);
		try {
			if(clerkEmailAddresses.size() > 0){
				this.mailService.send(clerkEmailAddresses.toArray(new String[]{}), subject, mailContent, true);
			}
			
			this.mailService.send(userAccount.getEmailAddress(), subject, mailContent, true);
			
		} catch (MessagingException e) {
			log.error("send mail for clerks or customer failed", e);
		}
		
	}

	/**
	 * @return the templatePath
	 */
	public String getTemplatePath() {
		return templatePath;
	}

	/**
	 * @param templatePath the templatePath to set
	 */
	public void setTemplatePath(String templatePath) {
		this.templatePath = templatePath;
	}

}
